Skip to content

[Feature] Allow TabsTrigger to render as an anchor via as:#425

Merged
cirdes merged 3 commits into
ruby-ui:mainfrom
jacksonpires:feature/tabs-trigger-as-link
Jun 15, 2026
Merged

[Feature] Allow TabsTrigger to render as an anchor via as:#425
cirdes merged 3 commits into
ruby-ui:mainfrom
jacksonpires:feature/tabs-trigger-as-link

Conversation

@jacksonpires

@jacksonpires jacksonpires commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an as: option to RubyUI::TabsTrigger (default :button) so a trigger can render as an <a href> instead of a <button>.

This is useful when tabs are actually navigation links — e.g. server-rendered tabs driven by a query param (?tab=...). The common workaround is to wrap a TabsTrigger (<button>) inside a Link (<a>), which produces a <button> nested inside an <a>. That is invalid HTML and a keyboard/accessibility problem (nested interactive elements). With as: :a, each navigable tab collapses to a single <a> while keeping the trigger's styling and data-* attributes.

Behavior

  • Default is unchanged: TabsTrigger still renders <button type="button">, so existing client-side tabs are unaffected (fully backward compatible).
  • TabsTrigger(value: ..., as: :a, href: ...) renders <a href> with the same classes and data-* (data-ruby-ui--tabs-target="trigger", the click->ruby-ui--tabs#show action, and data-value).
  • type="button" is only emitted for the :button variant (it's invalid on an anchor).

The ruby-ui--tabs#show Stimulus controller does not call preventDefault, so the anchor navigates normally while the active state stays driven by the data-* attributes.

Usage

Tabs(default_value: "account", class: "w-96") do
  TabsList do
    TabsTrigger(value: "account", as: :a, href: "?tab=account") { "Account" }
    TabsTrigger(value: "password", as: :a, href: "?tab=password") { "Password" }
  end
end

Changes

  • gem/lib/ruby_ui/tabs/tabs_trigger.rb — add as: option and conditional element/type rendering.
  • gem/test/ruby_ui/tabs_trigger_test.rb — new tests: renders as <button> by default, renders as <a href> with as: :a, and preserves trigger data-* on the anchor.
  • docs/app/views/docs/tabs.rb — add an "As navigation links" example.

How to test

cd gem
bundle exec rake test TEST=test/ruby_ui/tabs_trigger_test.rb
bundle exec rake   # full suite + standardrb

🤖 Generated with Claude Code


Summary by cubic

Add as: to RubyUI::TabsTrigger to render triggers as <a href> for navigation tabs; anchors keep trigger classes and data-* and navigate normally. Default remains <button type="button">, and we now always emit type="button" for any non-anchor trigger to prevent accidental form submissions (e.g., when as: is mistyped).

Written for commit f753dd8. Summary will update on new commits.

Review in cubic

Add an `as:` option to `RubyUI::TabsTrigger` (default `:button`) so a
trigger can render as `<a href>` for tabs that are navigation links.
This avoids nesting a `<button>` inside an `<a>` (invalid HTML and a
keyboard/accessibility issue) when tabs are server-rendered links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jacksonpires jacksonpires requested a review from cirdes as a code owner June 15, 2026 16:41

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread gem/lib/ruby_ui/tabs/tabs_trigger.rb Outdated
jacksonpires and others added 2 commits June 15, 2026 13:55
Tie the `type="button"` attribute to the element actually rendered
(`unless @as == :a`) instead of an exact `@as == :button` check. An
unexpected/mistyped `as:` value fell through to the `<button>` branch
without a type, which defaults to `type="submit"` and could trigger
accidental form submission. Issue identified by cubic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cirdes cirdes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@cirdes cirdes merged commit dea0047 into ruby-ui:main Jun 15, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants